package com.alinesno.cloud.initializr.starter.common.impl;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alinesno.cloud.common.core.utils.StringUtils;
import com.alinesno.cloud.initializr.starter.codegen.constant.Constants;
import com.alinesno.cloud.initializr.starter.codegen.constant.GenConstants;
import com.alinesno.cloud.initializr.starter.codegen.core.text.CharsetKit;
import com.alinesno.cloud.initializr.starter.codegen.core.text.Convert;
import com.alinesno.cloud.initializr.starter.codegen.entity.GenTable;
import com.alinesno.cloud.initializr.starter.codegen.entity.GenTableColumn;
import com.alinesno.cloud.initializr.starter.codegen.exception.BusinessException;
import com.alinesno.cloud.initializr.starter.codegen.seedgen.utils.ProjectVelocityUtils;
import com.alinesno.cloud.initializr.starter.codegen.service.IGenTableService;
import com.alinesno.cloud.initializr.starter.codegen.util.GenUtils;
import com.alinesno.cloud.initializr.starter.codegen.util.VelocityInitializer;
import com.alinesno.cloud.initializr.starter.codegen.util.VelocityUtils;
import com.alinesno.cloud.initializr.starter.codegen.utils.GenStringUtils;
import com.alinesno.cloud.initializr.starter.common.mapper.GenTableColumnMapper;
import com.alinesno.cloud.initializr.starter.common.mapper.GenTableMapper;
import com.alinesno.cloud.initializr.starter.dto.GenInfoDto;
import com.alinesno.cloud.initializr.starter.dynamicSqlSession.SqlSessionFactoryUtil;
import com.alinesno.cloud.initializr.starter.entity.InitializrDbListEntity;
import com.alinesno.cloud.initializr.starter.seedgen.bean.ProjectInfoDto;

/**
 * 业务 服务层实现
 *
 * @author ruoyi
 */
@Service
public class GenTableServiceImpl implements IGenTableService {
	private static final Logger log = LoggerFactory.getLogger(GenTableServiceImpl.class);

	@Autowired
	private GenTableMapper genTableMapper;

	@Autowired
	private GenTableColumnMapper genTableColumnMapper;

	/**
	 * 查询业务信息
	 *
	 * @param id 业务ID
	 * @return 业务信息
	 */
	@Override
	public GenTable selectGenTableById(Long id) {
		GenTable genTable = genTableMapper.selectGenTableById(id);
		setTableFromOptions(genTable);
		return genTable;
	}

	/**
	 * 查询业务列表
	 *
	 * @param genTable 业务信息
	 * @return 业务集合
	 */
	@Override
	public List<GenTable> selectGenTableList(GenTable genTable) {
		return genTableMapper.selectGenTableList(genTable);
	}

	/**
	 * 查询数据库列表
	 *
	 * @return
	 */
	@Override
	public List<String> listAllDb() {
		return genTableMapper.listAllDb();
	}

	/**
	 * 查询据库列表
	 *
	 * @param genTable 业务信息
	 * @return 数据库表集合
	 */
	@Override
	public List<GenTable> selectDbTableList(GenTable genTable) {
		return genTableMapper.selectDbTableList(genTable);
	}

	/**
	 * 查询据库列表
	 *
	 * @param tableNames 表名称组
	 * @return 数据库表集合
	 */
	@Override
	public List<GenTable> selectDbTableListByNames(String[] tableNames, String[] tableSchema) {
		return genTableMapper.selectDbTableListByNames(tableNames, tableSchema);
	}

	/**
	 * 查询所有表信息
	 *
	 * @return 表信息集合
	 */
	@Override
	public List<GenTable> selectGenTableAll() {
		return genTableMapper.selectGenTableAll();
	}

	/**
	 * 修改业务
	 *
	 * @param genTable 业务信息
	 * @return 结果
	 */
	@Override
	@Transactional
	public void updateGenTable(GenTable genTable) {
		String options = JSON.toJSONString(genTable.getParams());
		genTable.setOptions(options);
		int row = genTableMapper.updateGenTable(genTable);
		if (row > 0) {
			for (GenTableColumn cenTableColumn : genTable.getColumns()) {
				genTableColumnMapper.updateGenTableColumn(cenTableColumn);
			}
		}
	}

	/**
	 * 删除业务对象
	 *
	 * @param ids 需要删除的数据ID
	 * @return 结果
	 */
	@Override
	@Transactional
	public void deleteGenTableByIds(String ids) {
		genTableMapper.deleteGenTableByIds(Convert.toLongArray(ids));
		genTableColumnMapper.deleteGenTableColumnByIds(Convert.toLongArray(ids));
	}

	/**
	 * 导入表结构
	 *
	 * @param tableList 导入表列表
	 * @param operName  操作人员
	 */
	@Override
	@Transactional
	public void importGenTable(List<GenTable> tableList, String operName) {
		try {
			for (GenTable table : tableList) {
				String tableName = table.getTableName();
				String tableSchema = table.getTableSchema();
				GenUtils.initTable(table, operName);
				int row = genTableMapper.insertGenTable(table);
				if (row > 0) {
					// 保存列信息
					List<GenTableColumn> genTableColumns = genTableColumnMapper
							.selectDbTableColumnsByNameAndSchema(tableName, tableSchema);
					genTableColumns = new SuperEntityFilter().apply(genTableColumns);
					for (GenTableColumn column : genTableColumns) {
						GenUtils.initColumnField(column, table);
						column.setTableSchema(tableSchema);
						genTableColumnMapper.insertGenTableColumn(column);
					}
				}
			}
		} catch (Exception e) {
			throw new BusinessException("导入失败：" + e.getMessage());
		}
	}

	/**
	 * 预览代码
	 *
	 * @param tableId 表编号
	 * @return 预览数据列表
	 */
	@Override
	public Map<String, String> previewCode(Long tableId) {
		Map<String, String> dataMap = new LinkedHashMap<>();
		// 查询表信息
		GenTable table = genTableMapper.selectGenTableById(tableId);
		// 设置主子表信息
		setSubTable(table);
		// 设置主键列信息
		setPkColumn(table);
		VelocityInitializer.initVelocity();

		VelocityContext context = VelocityUtils.prepareContext(table);

		// 获取模板列表
		List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
		for (String template : templates) {
			// 渲染模板
			StringWriter sw = new StringWriter();
			Template tpl = Velocity.getTemplate(template, Constants.UTF8);
			tpl.merge(context, sw);
			dataMap.put(template, sw.toString());
		}
		return dataMap;
	}

	/**
	 * 生成代码（下载方式）
	 *
	 * @param tableName 表名称
	 * @return 数据
	 */
	@SuppressWarnings("deprecation")
	@Override
	public byte[] downloadCode(String tableName, String tableSchema) {
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		ZipOutputStream zip = new ZipOutputStream(outputStream);
		generatorCode(tableName, tableSchema, zip , null);
		IOUtils.closeQuietly(zip);
		return outputStream.toByteArray();
	}

	/**
	 * 生成代码（自定义路径）
	 *
	 * @param tableName 表名称
	 */
	@Override
	public void generatorCode(String tableName, String tableSchema) {
		// 查询表信息
		GenTable table = genTableMapper.selectGenTableByName(tableName, tableSchema);
		// 设置主子表信息
		setSubTable(table);
		// 设置主键列信息
		setPkColumn(table);

		VelocityInitializer.initVelocity();

		VelocityContext context = VelocityUtils.prepareContext(table);

		// 获取模板列表
		List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
		for (String template : templates) {
			if (!GenStringUtils.contains(template, "sql.vm")) {
				// 渲染模板
				StringWriter sw = new StringWriter();
				Template tpl = Velocity.getTemplate(template, Constants.UTF8);
				tpl.merge(context, sw);
				try {
					String path = getGenPath(table, template);
					FileUtils.writeStringToFile(new File(path), sw.toString(), CharsetKit.UTF_8);
				} catch (IOException e) {
					throw new BusinessException("渲染模板失败，表名：" + table.getTableName());
				}
			}
		}
	}

	/**
	 * 同步数据库
	 *
	 * @param tableName 表名称
	 */
	@Override
	@Transactional
	public void synchDb(String tableName, String tableSchema) {
		GenTable table = genTableMapper.selectGenTableByName(tableName, tableName);
		List<GenTableColumn> tableColumns = table.getColumns();
		List<String> tableColumnNames = tableColumns.stream().map(GenTableColumn::getColumnName)
				.collect(Collectors.toList());

		List<GenTableColumn> dbTableColumns = genTableColumnMapper.selectDbTableColumnsByNameAndSchema(tableName,
				tableSchema);
		dbTableColumns = new SuperEntityFilter().apply(dbTableColumns);
		if (GenStringUtils.isEmpty(dbTableColumns)) {
			throw new BusinessException("同步数据失败，原表结构不存在");
		}
		List<String> dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName)
				.collect(Collectors.toList());

		dbTableColumns.forEach(column -> {
			if (!tableColumnNames.contains(column.getColumnName())) {
				GenUtils.initColumnField(column, table);
				genTableColumnMapper.insertGenTableColumn(column);
			}
		});

		List<GenTableColumn> delColumns = tableColumns.stream()
				.filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList());
		if (GenStringUtils.isNotEmpty(delColumns)) {
			genTableColumnMapper.deleteGenTableColumns(delColumns);
		}
	}

	/**
	 * 批量生成代码
	 *
	 * @param tableNames 表数组
	 * @return 数据
	 */
	@Override
	public byte[] downloadCode(String[] tableNames, String tableSchema , GenInfoDto genInfo ) {
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		try (ZipOutputStream zip = new ZipOutputStream(outputStream);){
			for (String tableName : tableNames) {
				generatorCode(tableName, tableSchema, zip , genInfo);
			}
		} catch (Exception e) {
			log.error("压缩异常:{}" , e);
		} finally {
			
		}
		return outputStream.toByteArray();
	}

	/**
	 * 查询表信息并生成代码
	 */
	@SuppressWarnings("deprecation")
	private void generatorCode(String tableName, String tableSchema, ZipOutputStream zip , GenInfoDto genInfo) {
		// 查询表信息
		GenTable table = genTableMapper.selectGenTableByName(tableName, tableSchema);
		
		// 设置主子表信息
		setSubTable(table);
		
		// 设置主键列信息
		setPkColumn(table);
		
		// 设置构建信息
		setGenInfo(table , genInfo) ; 

		VelocityInitializer.initVelocity();
		VelocityContext context = VelocityUtils.prepareContext(table);

		// 获取模板列表
		List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
		for (String template : templates) {
			// 渲染模板
			StringWriter sw = new StringWriter();
			Template tpl = Velocity.getTemplate(template, Constants.UTF8);
			tpl.merge(context, sw);
			try {
				String fname = VelocityUtils.getFileName(template, table);

				if (StringUtils.isBlank(fname)) {
					continue;
				}
				
				log.debug("fname:{}" , fname);

				// 添加到zip
				zip.putNextEntry(new ZipEntry(fname));
				IOUtils.write(sw.toString(), zip, Constants.UTF8);
				IOUtils.closeQuietly(sw);
				
				zip.flush();
				zip.closeEntry();
			} catch (IOException e) {
				log.error("渲染模板失败，表名：" + table.getTableName(), e);
			}
		}
	}

	private void setGenInfo(GenTable table, GenInfoDto genInfo) {
		if(genInfo != null) {
			if(StringUtils.isNotEmpty(genInfo.getPackagePath())) {
				table.setPackageName(genInfo.getPackagePath());
			}
			if(StringUtils.isNotEmpty(genInfo.getAuthor())) {
				table.setFunctionAuthor(genInfo.getAuthor());
			}
			if(StringUtils.isNotEmpty(genInfo.getModuleName())) {
				table.setModuleName(genInfo.getModuleName());
			}
		}
	}

	/**
	 * 修改保存参数校验
	 *
	 * @param genTable 业务信息
	 */
	@Override
	public void validateEdit(GenTable genTable) {
		if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) {
			String options = JSON.toJSONString(genTable.getParams());
			JSONObject paramsObj = JSONObject.parseObject(options);
			if (GenStringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) {
				throw new BusinessException("树编码字段不能为空");
			} else if (GenStringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) {
				throw new BusinessException("树父编码字段不能为空");
			} else if (GenStringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) {
				throw new BusinessException("树名称字段不能为空");
			}
		} else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) {
			if (GenStringUtils.isEmpty(genTable.getSubTableName())) {
				throw new BusinessException("关联子表的表名不能为空");
			} else if (GenStringUtils.isEmpty(genTable.getSubTableFkName())) {
				throw new BusinessException("子表关联的外键名不能为空");
			}
		}
	}

	@Override
	public void delOldImport(InitializrDbListEntity entity) {
		genTableMapper.delByTableSchema(entity.getDbName());
		genTableColumnMapper.delByTableSchema(entity.getDbName());
	}

	@Override
	public void updateMeta(GenTable genTable) {
		genTableMapper.updateMeta(genTable);
	}

	@Override
	public List<GenTable> selectTablesByDbName(String dbName) {
		return genTableMapper.selectTablesByDbName(dbName);
	}

	@Override
	public void importGenTable(List<GenTable> tableList, InitializrDbListEntity entity, String operName) {
		SqlSession sqlSession = null;
		try {

			for (GenTable table : tableList) {
				String tableName = table.getTableName();
				String tableSchema = table.getTableSchema();
				GenUtils.initTable(table, operName);
				int row = genTableMapper.insertGenTable(table);
				if (row > 0) {
					if (sqlSession == null) {
						// 防止开启过多连接导致服务器拒绝
						sqlSession = new SqlSessionFactoryUtil().creatSession(entity);
					}
					GenTableColumnMapper thirdPartMapper = sqlSession.getMapper(GenTableColumnMapper.class);
					// 保存列信息
					List<GenTableColumn> genTableColumns = thirdPartMapper
							.selectDbTableColumnsByNameAndSchema(tableName, tableSchema);
					genTableColumns = new SuperEntityFilter().apply(genTableColumns);
					for (GenTableColumn column : genTableColumns) {
						GenUtils.initColumnField(column, table);
						column.setTableSchema(tableSchema);
						genTableColumnMapper.insertGenTableColumn(column);
					}
				}
			}
		} catch (Exception e) {
			throw new BusinessException("导入失败：" + e.getMessage());
		} finally {
			if (sqlSession != null) {
				sqlSession.close();
			}
		}
	}

	/**
	 * 设置主键列信息
	 *
	 * @param table 业务表信息
	 */
	public void setPkColumn(GenTable table) {
		for (GenTableColumn column : table.getColumns()) {
			if (column.isPk()) {
				table.setPkColumn(column);
				break;
			}
		}
		if (GenStringUtils.isNull(table.getPkColumn())) {
			table.setPkColumn(table.getColumns().get(0));
		}
		if (GenConstants.TPL_SUB.equals(table.getTplCategory())) {
			for (GenTableColumn column : table.getSubTable().getColumns()) {
				if (column.isPk()) {
					table.getSubTable().setPkColumn(column);
					break;
				}
			}
			if (GenStringUtils.isNull(table.getSubTable().getPkColumn())) {
				table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0));
			}
		}
	}

	/**
	 * 设置主子表信息
	 *
	 * @param table 业务表信息
	 */
	public void setSubTable(GenTable table) {
		String subTableName = table.getSubTableName();
		if (GenStringUtils.isNotEmpty(subTableName)) {
//            table.setSubTable(genTableMapper.selectGenTableByName(tableName, subTableName));
		}
	}

	/**
	 * 设置代码生成其他选项值
	 *
	 * @param genTable 设置后的生成对象
	 */
	public void setTableFromOptions(GenTable genTable) {
		JSONObject paramsObj = JSONObject.parseObject(genTable.getOptions());
		if (GenStringUtils.isNotNull(paramsObj)) {
			String treeCode = paramsObj.getString(GenConstants.TREE_CODE);
			String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE);
			String treeName = paramsObj.getString(GenConstants.TREE_NAME);
			String parentMenuId = paramsObj.getString(GenConstants.PARENT_MENU_ID);
			String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME);

			genTable.setTreeCode(treeCode);
			genTable.setTreeParentCode(treeParentCode);
			genTable.setTreeName(treeName);
			genTable.setParentMenuId(parentMenuId);
			genTable.setParentMenuName(parentMenuName);
		}
	}

	/**
	 * 获取代码生成地址
	 *
	 * @param table    业务表信息
	 * @param template 模板文件路径
	 * @return 生成地址
	 */
	public static String getGenPath(GenTable table, String template) {
		String genPath = table.getGenPath();
		if (GenStringUtils.equals(genPath, "/")) {
			return System.getProperty("user.dir") + File.separator + "src" + File.separator
					+ VelocityUtils.getFileName(template, table);
		}
		return genPath + File.separator + VelocityUtils.getFileName(template, table);
	}

	public GenTableMapper getGenTableMapper() {
		return genTableMapper;
	}

	public void setGenTableMapper(GenTableMapper genTableMapper) {
		this.genTableMapper = genTableMapper;
	}

	public GenTableColumnMapper getGenTableColumnMapper() {
		return genTableColumnMapper;
	}

	public void setGenTableColumnMapper(GenTableColumnMapper genTableColumnMapper) {
		this.genTableColumnMapper = genTableColumnMapper;
	}

	@Override
	public List<GenTable> selectDbTableListByNames(String[] tableNames) {
		return null;
	}

	@Override
	public void importGenTable(List<GenTable> tableList) {

	}

	@Override
	public void deleteGenTableByIds(Long[] tableIds) {
		genTableMapper.deleteGenTableByIds(tableIds);
	}

	@Override
	public byte[] downloadCode(String tableName) {
		return null;
	}

	@Override
	public void generatorCode(String tableName) {

	}

	@Override
	public void synchDb(String tableName) {

	}

	@Override
	public byte[] downloadCode(String[] tableNames) {
		return null;
	}

	@Override
	public byte[] generatorSeed(ProjectInfoDto dto) {
		return ProjectVelocityUtils.genJavaProjectSeed(dto);
	}

	@Override
	public byte[] generatorNpmSeed(ProjectInfoDto info) {
		return ProjectVelocityUtils.generatorNpmSeed(info);
	}

	@Override
	public List<GenTable> selectGenTableAll(List<String> tableStr) {
		return genTableMapper.selectGenTableAllByDb(tableStr);
	}

	
}